﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PSE.Framework.ProviderLoader.Configuration;
using PSE.AuditLogging.LogEntry;
using PSE.Framework.ProviderLoader;
using PSE.AuditLogging.Common;
using PSE.AuditLogging.Common.Exception;

namespace PSE.AuditLogging.Repository.Manager
{
    public class TemporaryRepositoryManager : IDisposable, ITemporaryRepository
    {
        public string RepositoryId
        {
            get;
            private set;
        }

        private ITemporaryRepository _provider;
        private OperationRetryControl _operationRetryControl;

        public TemporaryRepositoryManager()
        {
            _operationRetryControl = new OperationRetryControl();
            _operationRetryControl.AddRetryControlInformation("send", new TimeSpan(0, 0, 0, 0, 200), 5);
            _operationRetryControl.AddRetryControlInformation("receive", new TimeSpan(0, 0, 0, 0, 200), 5);
            _operationRetryControl.AddRetryControlInformation("peek", new TimeSpan(0, 0, 0, 0, 200), 5);
            _operationRetryControl.AddRetryControlInformation("remove", new TimeSpan(0, 0, 0, 0, 200), 5);
        }

        /// <summary>
        /// Carregar um provider de acordo com a configuração informada
        /// </summary>
        /// <param name="configuration"></param>
        public void LoadProvider(ProviderConfigurationElement configuration)
        {
            if (configuration == null)
                throw new ArgumentNullException("configuration");
            else
            {
                //se provider ainda n foi carregado
                if (_provider == null)
                {
                    _provider = ProviderLoader<ITemporaryRepository>.LoadProvider(configuration);
                }
                else
                    throw new InvalidOperationException(string.Format("The provider '{0}' was already loaded.", configuration.Name));
            }
        }

        #region IDisposable Members
        public void Dispose()
        {
            if (_provider != null)
                _provider.Dispose();

            _provider = null;
        }
        #endregion

        #region ITemporaryRepository Members
        /// <summary>
        /// Repassa a chamada do método para o provider correto
        /// </summary>
        /// <param name="entryPackage"></param>
        public void Send(LogEntryPackage entryPackage)
        {
            if (_provider == null)
                throw new InvalidOperationException("The method cannot be called. The provider was not loaded.");

            _operationRetryControl.Reset("send");

            //loop utilizado para reexecutar a chamada do metodo ate que seja executado com sucesso 
            //ou que o limite de tentativas seja atingido
            while (true)
            {
                try
                {
                    _provider.Send(entryPackage);
                    break;//parar o loop de "reexecucao"
                }
                catch (UnavailableRepositoryException)
                {
                    if (_operationRetryControl.CanRetry("send"))
                    {
                        continue;
                    }
                    else
                    {
                        ///nao conseguiu executar apos todas as tentativas
                        throw;
                    }
                }
            }
        }

        /// <summary>
        /// Repassa a chamada do método para o provider correto
        /// </summary>
        /// <returns></returns>
        public LogEntryPackage Peek()
        {
            if (_provider == null)
                throw new InvalidOperationException("The method cannot be called. The provider was not loaded.");

            _operationRetryControl.Reset("peek");

            //loop utilizado para reexecutar a chamada do metodo ate que seja executado com sucesso 
            //ou que o limite de tentativas seja atingido
            while (true)
            {
                try
                {
                    return _provider.Peek();
                }
                catch (UnavailableRepositoryException)
                {///se o repositorio estiver indisponivel, verificar se pode retentar executar
                    if (_operationRetryControl.CanRetry("peek"))
                    {
                        continue;
                    }
                    else
                    {
                        ///nao conseguiu executar apos todas as tentativas
                        throw;
                    }
                }
            }
        }

        /// <summary>
        /// Repassa a chamada do método para o provider correto
        /// </summary>
        /// <returns></returns>
        public LogEntryPackage Receive()
        {
            if (_provider == null)
                throw new InvalidOperationException("The method cannot be called. The provider was not loaded.");

            _operationRetryControl.Reset("receive");

            //loop utilizado para reexecutar a chamada do metodo ate que seja executado com sucesso 
            //ou que o limite de tentativas seja atingido
            while (true)
            {
                try
                {
                    return _provider.Receive();
                }
                catch (UnavailableRepositoryException)
                {///se o repositorio estiver indisponivel, verificar se pode retentar executar
                    if (_operationRetryControl.CanRetry("receive"))
                    {
                        continue;
                    }
                    else
                    {
                        ///nao conseguiu executar apos todas as tentativas
                        throw;
                    }
                }
            }
        }

        /// <summary>
        /// Repassa a chamada do método para o provider correto
        /// </summary>
        public void RemoveTop()
        {
            if (_provider == null)
                throw new InvalidOperationException("The method cannot be called. The provider was not loaded.");

            _operationRetryControl.Reset("remove");

            //loop utilizado para reexecutar a chamada do metodo ate que seja executado com sucesso 
            //ou que o limite de tentativas seja atingido
            while (true)
            {
                try
                {
                    _provider.RemoveTop();
                    break;
                }
                catch (UnavailableRepositoryException)
                {///se o repositorio estiver indisponivel, verificar se pode retentar executar
                    if (_operationRetryControl.CanRetry("remove"))
                    {
                        continue;
                    }
                    else
                    {
                        ///nao conseguiu executar apos todas as tentativas
                        throw;
                    }
                }
            }
        }

        #endregion
    }
}
